package cn.schoolwow.quickdao.module.database.dql.subCondition.kit;

import cn.schoolwow.quickdao.domain.database.dql.condition.Condition;
import cn.schoolwow.quickdao.domain.database.dql.subCondition.LambdaSubCondition;
import cn.schoolwow.quickdao.domain.database.dql.subCondition.SubCondition;
import cn.schoolwow.quickdao.domain.database.parent.DatabaseType;
import cn.schoolwow.quickdao.domain.entity.Entity;
import cn.schoolwow.quickdao.domain.entity.Property;
import cn.schoolwow.quickdao.module.database.dql.condition.domain.SQLFragmentEntry;
import cn.schoolwow.quickdao.module.database.dql.subCondition.domain.SubQueryOnCondition;
import cn.schoolwow.quickdao.module.database.dql.subCondition.domain.SubQueryOption;

import java.io.Serializable;
import java.lang.reflect.Array;
import java.lang.reflect.Proxy;
import java.util.Arrays;
import java.util.Collection;
import java.util.Locale;

public class SubConditionImpl<T, P> implements SubCondition<T, P>, Serializable {
    public SubQueryOption subQueryOption;

    public SubConditionImpl(SubQueryOption subQueryOption) {
        this.subQueryOption = subQueryOption;
    }

    @Override
    public SubCondition<T, P> tableAliasName(String tableAliasName) {
        subQueryOption.subQueryTableOption.tableAliasName = tableAliasName;
        return this;
    }

    @Override
    public SubCondition<T, P> leftJoin() {
        subQueryOption.subQueryJoinTableOption.join = "left outer join";
        return this;
    }

    @Override
    public SubCondition<T, P> rightJoin() {
        switch (subQueryOption.subQueryJoinTableOption.queryOption.quickDAOConfig.databaseContext.databaseProvider.getDatabaseType()){
            case SQLite:{throw new UnsupportedOperationException("SQLite目前不支持右外连接和全外连接!");}
        }
        subQueryOption.subQueryJoinTableOption.join = "right outer join";
        return this;
    }

    @Override
    public SubCondition<T, P> fullJoin() {
        subQueryOption.subQueryJoinTableOption.join = "full outer join";
        return this;
    }

    @Override
    public SubCondition<T, P> on(String primaryField, String joinTableField) {
        return on(null, primaryField, joinTableField);
    }

    @Override
    public SubCondition<T, P> on(String primaryTableAliasName, String primaryField, String joinTableField) {
        SubQueryOnCondition subQueryOnCondition = new SubQueryOnCondition();

        subQueryOnCondition.parentTableAliasName = primaryTableAliasName;
        if(null!=primaryTableAliasName){
            //实际执行时查询表别名再进行关联
            subQueryOnCondition.parentTableFieldName = primaryField;
        }else if(null!=subQueryOption.subQueryJoinTableOption.parentSubQueryOption){
            //关联父表
            subQueryOnCondition.parentTableFieldName = subQueryOption.subQueryJoinTableOption.parentSubQueryOption.subQueryTableOption.entity.getColumnNameByFieldName(primaryField);
        }else{
            //关联主表
            subQueryOnCondition.parentTableFieldName = subQueryOption.subQueryJoinTableOption.queryOption.queryTableOption.entity.getColumnNameByFieldName(primaryField);
        }
        subQueryOnCondition.childTableFieldName = subQueryOption.subQueryTableOption.entity.getColumnNameByFieldName(joinTableField);
        subQueryOption.subQueryJoinTableOption.onConditionList.add(subQueryOnCondition);
        return this;
    }

    @Override
    public SubCondition<T, P> addNullQuery(String field) {
        subQueryOption.subQueryFragmentOption.whereList.add(new SQLFragmentEntry("and", field, "{} is null"));
        return this;
    }

    @Override
    public SubCondition<T, P> addNotNullQuery(String field) {
        subQueryOption.subQueryFragmentOption.whereList.add(new SQLFragmentEntry("and", field, "{} is not null"));
        return this;
    }

    @Override
    public SubCondition<T, P> addEmptyQuery(String field) {
        subQueryOption.subQueryFragmentOption.whereList.add(new SQLFragmentEntry("and", field, "{} is not null and {} = ''"));
        return this;
    }

    @Override
    public SubCondition<T, P> addNotEmptyQuery(String field) {
        subQueryOption.subQueryFragmentOption.whereList.add(new SQLFragmentEntry("and", field, "{} is not null and {} != ''"));
        return this;
    }

    @Override
    public SubCondition<T, P> addInQuery(String field, Object... values) {
        addInQuery(field, values, "in");
        return this;
    }

    @Override
    public SubCondition<T, P> addInQuery(String field, Collection values) {
        return addInQuery(field, values.toArray(new Object[0]));
    }

    @Override
    public SubCondition<T, P> addNotInQuery(String field, Object... values) {
        addInQuery(field, values, "not in");
        return this;
    }

    @Override
    public SubCondition<T, P> addNotInQuery(String field, Collection values) {
        return addNotInQuery(field, values.toArray(new Object[0]));
    }

    @Override
    public SubCondition<T, P> addBetweenQuery(String field, Object start, Object end) {
        subQueryOption.subQueryFragmentOption.whereList.add(new SQLFragmentEntry("and", field, "{} between ? and ?"));
        subQueryOption.subQueryFragmentOption.whereParameterList.add(start);
        subQueryOption.subQueryFragmentOption.whereParameterList.add(end);
        return this;
    }

    @Override
    public SubCondition<T, P> addLikeQuery(String field, Object value) {
        if (value == null || value.toString().equals("")) {
            return this;
        }
        subQueryOption.subQueryFragmentOption.whereList.add(new SQLFragmentEntry("and", field, "{} like ?"));
        subQueryOption.subQueryFragmentOption.whereParameterList.add(value);
        return this;
    }

    @Override
    public SubCondition<T, P> addNotLikeQuery(String field, Object value) {
        if (value == null || value.toString().equals("")) {
            return this;
        }
        subQueryOption.subQueryFragmentOption.whereList.add(new SQLFragmentEntry("and", field, "{} not like ?"));
        subQueryOption.subQueryFragmentOption.whereParameterList.add(value);
        return this;
    }

    @Override
    public SubCondition<T, P> addSubQuery(String field, Condition subQuery) {
        return addSubQuery(field, "=", subQuery);
    }

    @Override
    public SubCondition<T, P> addSubQuery(String field, String operator, Condition subQuery) {
        if(null!=subQueryOption.subQueryTableOption.entity){
            field = subQueryOption.subQueryTableOption.entity.getColumnNameByFieldName(field);
        }
        subQueryOption.subQueryJoinTableOption.condition.addSubQuery(field, operator, subQuery);
        return this;
    }

    @Override
    public SubCondition<T, P> addQuery(String field, Object value) {
        addQuery(field, "=", value);
        return this;
    }

    @Override
    public SubCondition<T, P> addQuery(String field, String operator, Object value) {
        if (null == value) {
            addNullQuery(field);
        } else if (value.toString().isEmpty()) {
            addEmptyQuery(field);
        } else {
            subQueryOption.subQueryFragmentOption.whereList.add(new SQLFragmentEntry("and", field, "{} " + operator + " ?"));
            subQueryOption.subQueryFragmentOption.whereParameterList.add(value);
        }
        return this;
    }

    @Override
    public SubCondition<T, P> addIntervalQuery(String field, String value) {
        if(!value.contains(",")){
            return this;
        }
        Long priceStart = Long.parseLong(value.substring(1,value.indexOf(",")));
        Long priceEnd = Long.parseLong(value.substring(value.indexOf(",")+1, value.length()-1));
        if(priceEnd<priceStart){
            throw new IllegalArgumentException("开闭区间不合法!请检查!开闭区间值:"+value);
        }
        switch (value.charAt(0)){
            case '(':addQuery(field, ">", priceStart);break;
            case '[':addQuery(field, ">=", priceStart);break;
            default:{
                throw new IllegalArgumentException("开闭区间开始值必须为'('或者'['!当前值:"+value.charAt(0));
            }
        }
        switch (value.charAt(value.length()-1)){
            case ')':addQuery(field, "<", priceEnd);break;
            case ']':addQuery(field, "<=", priceEnd);break;
            default:{
                throw new IllegalArgumentException("开闭区间结束值必须为')'或者']'!当前值:"+value.charAt(value.length()-1));
            }
        }
        return this;
    }

    @Override
    public SubCondition<T, P> addRawQuery(String query, Object... parameterList) {
        subQueryOption.subQueryFragmentOption.whereList.add(new SQLFragmentEntry("and", null, query));
        subQueryOption.subQueryFragmentOption.whereParameterList.addAll(Arrays.asList(parameterList));
        return this;
    }

    @Override
    public SubCondition<T, P> addJSONQuery(String field, String jsonPath, String operator, Object value) {
        if(null==operator){
            operator = "=";
        }
        String fieldQuery = "?";
        //支持in查询
        switch (operator.toLowerCase(Locale.ROOT).trim()){
            case "in":
            case "not in":{
                int length = -1;
                if(value instanceof Arrays){
                    length = Array.getLength(value);
                    for(int i=0;i<length;i++){
                        Object instance = Array.get(value,i);
                        subQueryOption.subQueryFragmentOption.whereParameterList.add(instance);
                    }
                }else if(value instanceof Collection){
                    Collection collection = (Collection)value;
                    length = collection.size();
                    subQueryOption.subQueryFragmentOption.whereParameterList.addAll(collection);
                }else{
                    throw new IllegalArgumentException("in查询的参数必须为数组或者集合");
                }
                StringBuilder fieldQueryBuilder = new StringBuilder("(");
                for (int i = 0; i < length; i++) {
                    fieldQueryBuilder.append("?,");
                }
                fieldQueryBuilder.deleteCharAt(fieldQueryBuilder.length() - 1);
                fieldQueryBuilder.append(")");
                fieldQuery = fieldQueryBuilder.toString();
            }break;
            default:{
                subQueryOption.subQueryFragmentOption.whereParameterList.add(value);
            }
        }

        DatabaseType databaseType = subQueryOption.subQueryJoinTableOption.queryOption.quickDAOConfig.databaseContext.databaseProvider.getDatabaseType();
        switch (databaseType){
            case Mysql:{
                subQueryOption.subQueryFragmentOption.whereList.add(new SQLFragmentEntry("and", field, "json_extract({},'$."+jsonPath+"') "+operator+" "+fieldQuery));
            }break;
            case Postgresql:{
                subQueryOption.subQueryFragmentOption.whereList.add(new SQLFragmentEntry("and", field, "{} ->> '"+jsonPath+"' "+operator+" "+fieldQuery));
            }break;
            default:{
                throw new IllegalArgumentException("暂不支持当前数据库JSON查询语法!数据库:"+databaseType.name());
            }
        }
        return this;
    }

    @Override
    public SubCondition<T,P> addCompositeQuery(String field, String value) {
        if(value.startsWith("null:")){
            addNullQuery(field);
        }else if(value.startsWith("not null:")){
            addNotNullQuery(field);
        }else if(value.startsWith("empty:")){
            addEmptyQuery(field);
        }else if(value.startsWith("not empty:")){
            addNotEmptyQuery(field);
        }else if(value.startsWith("like:")){
            addLikeQuery(field, value.substring(5));
        }else if(value.startsWith("not like:")){
            addNotLikeQuery(field, value.substring(9));
        }else if(value.startsWith("in:")){
            addInQuery(field, Arrays.asList(value.substring(3).split(",", -1)));
        }else if(value.startsWith("not in:")){
            addNotInQuery(field, value.substring(7));
        }else if(value.startsWith("interval:")){
            addIntervalQuery(field, value.substring(9));
        }else if(value.startsWith("between:")){
            String start = value.substring(8,value.indexOf(","));
            String end = value.substring(value.indexOf(",")+1);
            addBetweenQuery(field, start, end);
        }else if(value.startsWith("gt:")){
            addQuery(field, ">", value.substring(3));
        }else if(value.startsWith("gte:")){
            addQuery(field, ">=", value.substring(4));
        }else if(value.startsWith("lt:")){
            addQuery(field, "<", value.substring(3));
        }else if(value.startsWith("lte:")){
            addQuery(field, "<=", value.substring(4));
        }else{
            addQuery(field, value);
        }
        return this;
    }

    @Override
    public SubCondition<T, P> addColumn(String... fields) {
        for (String field : fields) {
            subQueryOption.subQueryColumnOption.columnList.add(field);
        }
        return this;
    }

    @Override
    public SubCondition<T, P> addColumnAs(String column, String columnAliasName) {
        return addColumn(column + " as " + subQueryOption.subQueryJoinTableOption.queryOption.quickDAOConfig.databaseContext.databaseProvider.escape(columnAliasName));
    }

    @Override
    public SubCondition<T, P> addTableColumn() {
        for(Property property:subQueryOption.subQueryTableOption.entity.properties){
            if(null==property.name||property.name.isEmpty()){
                subQueryOption.subQueryColumnOption.columnList.add(property.column);
            }else{
                subQueryOption.subQueryColumnOption.columnList.add(property.column+" "+property.name);
            }
        }
        return this;
    }

    @Override
    public SubCondition<T, P> addExcludeColumn(String... excludeFields) {
        for (String excludeField : excludeFields) {
            subQueryOption.subQueryColumnOption.excludeColumnList.add(excludeField);
        }
        return this;
    }

    @Override
    public <E> SubCondition<E, T> joinTable(Class<E> clazz, String primaryField, String joinTableField) {
        return joinTable(clazz, primaryField, joinTableField, subQueryOption.subQueryTableOption.entity.getCompositeFieldName(clazz.getName()));
    }

    @Override
    public <E> SubCondition<E, T> joinTable(Class<E> clazz, String primaryField, String joinTableField, String compositeFieldName) {
        Entity entity = subQueryOption.subQueryJoinTableOption.queryOption.quickDAOConfig.getEntityByClassName(clazz.getName());
        if (null == entity) {
            throw new IllegalArgumentException("未扫描到指定实体类!类名:" + clazz.getName());
        }
        primaryField = subQueryOption.subQueryTableOption.entity.getColumnNameByFieldName(primaryField);
        joinTableField = entity.getColumnNameByFieldName(joinTableField);

        SubConditionImpl<E,T> subCondition = (SubConditionImpl<E, T>) subQueryOption.subQueryJoinTableOption.condition.joinTable(clazz, primaryField, joinTableField, compositeFieldName);
        subCondition.subQueryOption.subQueryJoinTableOption.parentCondition = this;
        subCondition.subQueryOption.subQueryJoinTableOption.parentSubQueryOption = this.subQueryOption;
        return subCondition;
    }

    @Override
    public SubCondition<?, T> joinTable(String tableName, String primaryField, String joinTableField) {
        SubConditionImpl<?,T> subCondition = (SubConditionImpl<?, T>) subQueryOption.subQueryJoinTableOption.condition.joinTable(tableName, primaryField, joinTableField);
        subCondition.subQueryOption.subQueryJoinTableOption.parentCondition = this;
        subCondition.subQueryOption.subQueryJoinTableOption.parentSubQueryOption = this.subQueryOption;
        return subCondition;
    }

    @Override
    public SubCondition<T, P> groupBy(String... fields) {
        for (String field : fields) {
            subQueryOption.subQueryFragmentOption.groupByList.add(new SQLFragmentEntry(",", null, field));
        }
        return this;
    }

    @Override
    public SubCondition<T, P> order(String field, String asc) {
        subQueryOption.subQueryFragmentOption.orderByList.add(new SQLFragmentEntry(",", field, "{} " + asc));
        return this;
    }

    @Override
    public SubCondition<T, P> orderBy(String... fields) {
        for (String field : fields) {
            subQueryOption.subQueryFragmentOption.orderByList.add(new SQLFragmentEntry(",", field, "{} asc"));
        }
        return this;
    }

    @Override
    public SubCondition<T, P> orderByDesc(String... fields) {
        for (String field : fields) {
            subQueryOption.subQueryFragmentOption.orderByList.add(new SQLFragmentEntry(",", field, "{} desc"));
        }
        return this;
    }

    @Override
    public LambdaSubCondition<T, P> lambdaSubCondition() {
        LambdaSubConditionInvocationHandler<T, P> invocationHandler = new LambdaSubConditionInvocationHandler<T, P>(this);
        LambdaSubCondition<T, P> lambdaSubCondition = (LambdaSubCondition<T, P>) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), new Class<?>[]{LambdaSubCondition.class}, invocationHandler);
        return lambdaSubCondition;
    }

    @Override
    public SubCondition<P, ?> doneSubCondition() {
        return subQueryOption.subQueryJoinTableOption.parentCondition;
    }

    @Override
    public Condition<P> done() {
        return subQueryOption.subQueryJoinTableOption.condition;
    }

    @Override
    public String toString() {
        return subQueryOption.toString();
    }

    /**
     * 添加in查询
     */
    private void addInQuery(String field, Object[] values, String in) {
        if (null == values || values.length == 0) {
            return;
        }
        StringBuilder builder = new StringBuilder();
        builder.append(" {} " + in + " (");
        for (int i = 0; i < values.length; i++) {
            builder.append("?,");
        }
        builder.deleteCharAt(builder.length() - 1);
        builder.append(")");
        subQueryOption.subQueryFragmentOption.whereList.add(new SQLFragmentEntry("and", field, builder.toString()));
        subQueryOption.subQueryFragmentOption.whereParameterList.addAll(Arrays.asList(values));
    }
}
